package com.linran.webjsonrsp.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.security.Principal;
import java.time.LocalDateTime;

/**
 * websocket控制层
 * <p>
 * <p>
 * https://www.cnblogs.com/huangjinyong/p/10901806.html handler,beforeShakeHandle
 * https://www.baeldung.com/spring-websockets-sendtouser
 *
 * @author lin
 * @date 2020-08-28 10:29
 */
@Slf4j
@Controller
public class WebSocketAction {

    /**
     * 客户端推送消息并且同步回复
     * <p>
     * 接收客户端发送的消息，当客户端发送消息的目的地为/app/room/sendTest时，
     * 交给该注解所在的方法处理消息，其中/app是在WebSocketStompConfig中配置的前缀
     * <p>
     * 广播模式 @SendTo(value)
     * 若没有添加@SendTo注解且该方法有返回值，则返回的目的地地址为/topic/sendTest
     * ，经过消息代理，客户端需要订阅了这个主题才能收到返回消息
     * <p>
     * 点对点 @SendToUser(value)
     * 客户端订阅地址为/user/value,其中/user是WebSocketStompConfig中配置前缀
     *
     * @param message
     * @return 自动转json
     */
    /*广播模式-群发消息*/

//    @MessageMapping("/room/sendTest")
//    @SendTo("/topic/room/subscribeTest")
//    public ServerMessage sendDemo(ClientMessage message) {
//        log.info("接收到了信息" + message.getContent());
//        return new ServerMessage(LocalDateTime.now(), "系统中心", "你发送的消息为:" + message.getContent());
//    }

    /*点对点-对服务器发消息进行数据处理后，返回给原来的用户*/
    //去掉Principal,sessionId就是最简洁的点对点，也不用去生成Principal

    @MessageMapping("/room/sendTest")
    @SendToUser("/topic/room/subscribeTest")
    public ServerMessage sendDemo(@Payload ClientMessage message,
                                  Principal principal,
                                  @Header("simpSessionId") String sessionId) {
        log.info("sessionId:{} 接收到了来自[{}]信息:{}", sessionId, principal.getName(), message.getContent());
        return new ServerMessage(LocalDateTime.now(), "系统中心", message.getContent());
    }

    /**
     * 广播模式
     * 接收客户端发送的订阅，当客户端订阅的目的地为/app/room/subscribeTest时，交给该注解所在的方法处理订阅
     *
     * @return 自动转json
     */
    @SubscribeMapping("/room/subscribeTest")
    public ServerMessage sub(Principal principal) {
        //log.info(String.format("%s用户订阅了我。。。", principal.getName()));
        return new ServerMessage(LocalDateTime.now(), "系统中心", "感谢你订阅了我。。。");
    }

    /*以下代码可以放在任何地方,比如service层进行消息推送*/
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    //客户端只要订阅了/topic/subscribeTest主题，调用这个方法即可
    public void templateTest() {
        messagingTemplate.convertAndSend("/topic/room/subscribeTest", new ServerMessage("服务器主动推的数据"));
    }

    /*触发消息主动推送*/
    @GetMapping("/sendMsg")
    @ResponseBody
    public String sendMsg(String msg) {
        //广播
        messagingTemplate.convertAndSend("/topic/room/subscribeTest", new ServerMessage(msg));
        return String.format("消息：%s 发送成功", msg);
    }

    @GetMapping("sendMsgTo")
    @ResponseBody
    public String sendSpecific(String userName, String msg) throws Exception {
        ServerMessage out = new ServerMessage(
                LocalDateTime.now(),
                "系统1014",
                msg);
        //订阅-点对点
        messagingTemplate.convertAndSendToUser(
                userName, "/topic/room/subscribeTest", out);

        return String.format("消息：%s 发送成功", msg);
    }

    /*客户端订阅/user/{userId}/single_queue,直接调用方法convertAndSendToUser(userId,single_queue)就可以发消息;底层做了路径拼接,实际并未调通，还是需要生成Principal*/

    @GetMapping("sendMsgTo9517")
    @ResponseBody
    public String sendSpecific9517(String userId,String msg) throws Exception {
        ServerMessage out = new ServerMessage(
                LocalDateTime.now(),
                "系统9517",
                msg);
        //订阅
        messagingTemplate.convertAndSendToUser(
                userId, "/queue/single_queue", msg);

        return String.format("消息：%s 发送成功", msg);
    }

    /*以下为样例，未调试过*/

    @MessageMapping("/secured/room")
    public void sendSpecific(
            @Payload ClientMessage msg,
            Principal user,
            @Header("simpSessionId") String sessionId) throws Exception {
        ServerMessage out = new ServerMessage(
                LocalDateTime.now(),
                msg.getFrom(),
                msg.getContent());
        //订阅
        messagingTemplate.convertAndSendToUser(
                msg.getTo(), "/topic/room/subscribeTest", out);
    }
}
